package com.ecarx.module_udisk.controller;

import android.content.Context;
import android.os.Environment;
import android.os.StatFs;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;

import com.ecarx.library_base.base_util.FileUtils;
import com.ecarx.library_base.base_util.GsonUtils;
import com.ecarx.library_base.scoket.SocketManager;
import com.ecarx.library_base.scoket.callback.MsgCallback;
import com.ecarx.library_base.usb.UDiskDevice;
import com.ecarx.library_base.usb.UDiskManager;
import com.ecarx.library_base.usb.brodcast.USBReceiver;
import com.ecarx.module_udisk.bean.ImgUpdateRequest;
import com.ecarx.module_udisk.bean.LogBean;
import com.ecarx.module_udisk.bean.PathBean;
import com.ecarx.module_udisk.callback.ImgUpdateCallback;

import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

public class ImgUpdateController {
    private static final String TAG = ImgUpdateController.class.getSimpleName();
    private final Context mContext;
    private final ImgUpdateCallback mImgUpdateCallback;
    private static final String PROPERTY_UDISK_IMG_PATH = "ecarx.imgupdate.udiskfilepath";

    private static final String PROPERTY_DEST_IMG_PATH = "ecarx.imgupdate.destfilepath";

    private static final String PROPERTY_DEST_PARENT_IMG_PATH = "ecarx.fileupdate.destparentfilepath";
    private static final String PROPERTY_IMG_UPDATE_ENABLE = "persist.service.imgupdate.enable";

    private static final String PROPERTY_IMG_UPDATE_SUCCESS = "ecarx.imgupdate.success";
    private static final String PROPERTY_IMG_UPDATE_FAIL_REASON = "ecarx.fileupdate.failreason";

    private static final String IMGUPDATE_FAILED = "0";
    private static final String IMGUPDATE_UPDATING = "2";

    //安装日志
    private List<LogBean> mLogList = new ArrayList<>();
    private Map<Integer, String> taskMap = new HashMap<>();
    //最后一次安装APK的对象ID
    private int lastAPKSessionId;
    //APK安装文件存放目录
    private final String BASE_PATH = "/usb_upgrade/img_update/";

    private final String OTA_DOWNLOAD_PATH = "/data/ota_download/";
    private String mCurrFilePath = "Uxos";
    // 保存当前的U盘信息，用来处理在安装任务执行过程中U盘插拔的异常处理
    private UDiskDevice currentUDiskDevice;

    private static final int CHECK_PROPERTY_INTERVAL = 1000;
    private static final int FILE_UPDATE_INTERVAL = 100;


    private final SimpleDateFormat mFormatter = new SimpleDateFormat("HH:mm:ss.SSS");

    public ImgUpdateController(Context context, ImgUpdateCallback imgUpdateCallback) {
        mContext = context;
        mImgUpdateCallback = imgUpdateCallback;
        // U盘在工程模式起来之前已经插入系统
        dealDeviceInfo(UDiskManager.getInstance().getAllUDiskDevice());
        // 等待U盘接入回调
        UDiskManager.getInstance().addUDiskChangeListener(new USBReceiver.UsbChangeCallback() {
            @Override
            public void onUsbDeviceChanged(boolean isAdd, List<UDiskDevice> uDiskDeviceList) {
                dealDeviceInfo(uDiskDeviceList);
            }
        });
        try {
            SystemProperties.set("ctl.start", "mount_ota");
        } catch (RuntimeException e) {
            Log.e(TAG, e.getMessage(), e);
        }

    }

    private String stringWithTimeInfo(String oldStr) {
        return mFormatter.format(new Date(System.currentTimeMillis())) + "  " + oldStr;
    }

    public void setCurSelectSystem(String curSelectSystem) {
        this.mCurrFilePath = curSelectSystem;
    }

    public void imgStartUpload(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            Log.d(TAG, "installPathList filePath is null or 0 size");
            mImgUpdateCallback.imgUpdateLogEnd();
            return;
        }
        mLogList.add(new LogBean(stringWithTimeInfo(" img_update task start"), true));
        mImgUpdateCallback.imgUpdateLog(mLogList);
        long fileLength = new File(filePath).length();
        if (getOtaRomAvailable() <= 0) {
            mLogList.add(new LogBean(stringWithTimeInfo(" ota_download partition is not exit"), false));
            mImgUpdateCallback.imgUpdateLogEnd();
            return;
        }
        if (getOtaRomAvailable() - fileLength < 100) {
            mLogList.add(new LogBean(stringWithTimeInfo(" img_update not have enough space"), false));
            mImgUpdateCallback.imgUpdateLogEnd();
            return;
        }
        File file = new File(filePath);
        String s = OTA_DOWNLOAD_PATH + file.getName();
        boolean success = FileUtils.copyFile(file, new File(s));
        mLogList.add(new LogBean(stringWithTimeInfo("img_update " + file.getName() + "copy to ota_download " + (success ? "success" : "fail")), success));
        mImgUpdateCallback.imgUpdateLog(mLogList);
        mLogList.add(new LogBean(stringWithTimeInfo(" file_update task end"), true));
        mImgUpdateCallback.imgUpdateLog(mLogList);
        mImgUpdateCallback.imgUpdateLogEnd();
    }


    private void connectSocketAndSendMsg(String filePath) {
        if (!mCurrFilePath.equals("uxos")) {
            boolean connected = SocketManager.get().isConnected(SocketManager.ServerOS.Hypervisor_OS);
            if (!connected) {
                SocketManager.get().registerCallback(SocketManager.ServerOS.Hypervisor_OS, new MsgCallback() {
                    @Override
                    public void onConnected() {

                    }

                    @Override
                    public void onDisConnected() {

                    }

                    @Override
                    public void onConnectFail() {

                    }

                    @Override
                    public void onReceiver(int msgId, String msg) {

                    }

                    @Override
                    public void onFail(String reason) {

                    }
                });
                SocketManager.get().connectServer(SocketManager.ServerOS.Hypervisor_Update_OS);
            }
            String request = "";
            if (mCurrFilePath.equals("clusterOS")) {
                request = GsonUtils.toJson(new ImgUpdateRequest(2003, 2, filePath, System.currentTimeMillis(), "1.0.0"));
            } else if (mCurrFilePath.equals("gameOS")) {
                request = GsonUtils.toJson(new ImgUpdateRequest(2003, 1, filePath, System.currentTimeMillis(), "1.0.0"));

            }
            SocketManager.get().sendRequest(SocketManager.ServerOS.Hypervisor_Update_OS, request);
        }
    }

    /**
     * 解析获取到的U盘路径并将其组装成可按照文件路径
     *
     * @param mUDiskDeviceList
     */
    public void dealDeviceInfo(List<UDiskDevice> mUDiskDeviceList) {
        if (!mUDiskDeviceList.isEmpty()) {
            UDiskDevice tempUDiskDevice = mUDiskDeviceList.get(0);
            if (currentUDiskDevice == null) {
                currentUDiskDevice = tempUDiskDevice;
            } else {
                String curUUID = currentUDiskDevice.getUuid();
                String callbackUUID = tempUDiskDevice.getUuid();
                if (curUUID != null && !curUUID.equals(callbackUUID)) {
                    Log.d(TAG, "installTaskExecute is false, uDisk changed");

                    currentUDiskDevice = tempUDiskDevice;
                }
            }
            Log.d(TAG, "uDiskDevice info is: " + currentUDiskDevice.toString());
            mImgUpdateCallback.setUDiskInfo(currentUDiskDevice.getUSBName());
            analysisPathList(currentUDiskDevice.getPath());
        } else {
            mImgUpdateCallback.setUDiskInfo("");
            mImgUpdateCallback.setPathBeanList(null);
            Log.d(TAG, "installTaskExecute is false, uDisk missed");
        }
    }


    /**
     * 获取U盘指定路径下的apk文件
     *
     * @param path U盘绝根路径
     */
    public void analysisPathList(String path) {
        List<String> list = new ArrayList<>();
        list.add(".img");
        List<String> pathList = FileUtils.getFilteredFilePathWithOutParent(path, BASE_PATH + mCurrFilePath, list);
        List<PathBean> pathBeanList = new ArrayList<>();
        for (String s : pathList) {
            PathBean bean = new PathBean(path + s, s.substring(BASE_PATH.length() + mCurrFilePath.length() + 1));
            pathBeanList.add(bean);
        }
        mImgUpdateCallback.setPathBeanList(pathBeanList);
    }

    private void copyFile(String destFilePath) {
        try {
            String destParentFilePath = new File(destFilePath).getParentFile().toString();
            Log.d(TAG, "copyFile: destParentFilePath=" + destParentFilePath);
            SystemProperties.set(PROPERTY_IMG_UPDATE_ENABLE, "0");
            SystemProperties.set(PROPERTY_DEST_IMG_PATH, destFilePath);
            SystemProperties.set(PROPERTY_DEST_PARENT_IMG_PATH, destParentFilePath);
            SystemProperties.set(PROPERTY_IMG_UPDATE_ENABLE, "1");
            Thread.sleep(FILE_UPDATE_INTERVAL);
            while (true) {
                int s = SystemProperties.getInt(PROPERTY_IMG_UPDATE_SUCCESS, 2);
                String a = SystemProperties.get(PROPERTY_IMG_UPDATE_SUCCESS, "");
                if (!IMGUPDATE_UPDATING.equals(SystemProperties.get(PROPERTY_IMG_UPDATE_SUCCESS, ""))) {
                    break;
                }
                String result = destFilePath + " file_copy upgrading";
                mLogList.add(new LogBean(stringWithTimeInfo(result), true));
                mImgUpdateCallback.imgUpdateLog(mLogList);
                Thread.sleep(CHECK_PROPERTY_INTERVAL);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private long getOtaRomAvailable() {
        String path = Environment.getDataDirectory().getPath() + "/ota_download";
        if (!new File(path).exists()) {
            return 0l;
        }
        StatFs stat = new StatFs(path);
        long blockSize = stat.getBlockSizeLong();
        long availableBlocks = stat.getAvailableBlocksLong();
        long availableRom = blockSize * availableBlocks / 1048576;
        Log.d(TAG, "Available path=" + path + " blockSize : " + blockSize + ";   availableBlocks : " + availableBlocks + ";     " + "result : " + availableRom + "MB;");
        return availableRom;//返回M
    }

}
